#include "chatserver.h"

//初始化服务器 构造函数
ChatServer::ChatServer(QObject *parent) : QObject(parent), server(nullptr)
{
    // 初始化日志文件
    logFile.setFileName("chat_server.log");
    if (!logFile.open(QIODevice::WriteOnly | QIODevice::Append)) {
        qWarning() << "无法打开日志文件:" << logFile.errorString();
    }

    // 连接数据库
    connect2DB();
}
// 服务器关闭 析构函数
ChatServer::~ChatServer()
{
    // 关闭服务器
    if (server) {
        server->close();// 停止接受新连接
        delete server;// 释放内存
    }

    // 关闭所有客户端连接
    foreach (QTcpSocket *socket, clients) {
        socket->close();// 主动断开连接
        socket->deleteLater();// 延迟释放内存
    }

    // 关闭数据库
    if (db.isOpen()) {
        db.close();// 关闭数据库连接
    }

    // 关闭日志文件
    if (logFile.isOpen()) {
        logFile.close();// 确保缓冲区写入磁盘
    }
}
// 启动 TCP 服务器
void ChatServer::startServer(quint16 port)
{
    // 创建 QTcpServer 实例
    server = new QTcpServer(this);
    // 启动服务器监听
    // 尝试监听所有可用网络接口（QHostAddress::Any 即 0.0.0.0）
    if (!server->listen(QHostAddress::Any, port)) {
        printMsg("服务器启动失败: " + server->errorString());
        return;
    }

    // 信号槽连接
    //客户端连接处理函数
    connect(server, &QTcpServer::newConnection, this, &ChatServer::newConnectionSlot);
    printMsg(QString("服务器已启动，监听端口 %1").arg(port));
}

//日志文件
void ChatServer::printMsg(const QString &msg)
{
    // 时间戳生成
    QString dt = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
    QString logMsg = QString("%1 | %2").arg(dt,msg);

    // 输出到控制台
    qDebug().noquote() << logMsg;

    // 写入日志文件
    if (logFile.isOpen()) {
        QTextStream logStream(&logFile);
        logStream << logMsg << "\n";
        logStream.flush();
    }
}

//连接数据库
void ChatServer::connect2DB()
{
    //设置数据库驱动 指定使用 Qt 的 SQLite 驱动
    db = QSqlDatabase::addDatabase("QSQLITE");

    // 使用应用程序目录下的数据库文件
    QString dbPath = QCoreApplication::applicationDirPath() + "/chat_msg.db";
    db.setDatabaseName(dbPath);

    if (db.open()) {
        //打印日志
        printMsg("数据库连接成功: " + dbPath);
        createLoginTable();
        createChatTable();
    } else {
        printMsg("数据库连接失败: " + db.lastError().text());
    }
}
//创建登录表
void ChatServer::createLoginTable()
{
    // 创建SQL查询对象
    QSqlQuery sq;
    QString sql = "CREATE TABLE IF NOT EXISTS login_msg ("
                  "user_name TEXT PRIMARY KEY, "
                  "password TEXT NOT NULL)";

    if (!sq.exec(sql)) {
        printMsg("创建登录表失败: " + sq.lastError().text());
    } else {
        printMsg("登录表创建/检查完成");
    }
}
//创建聊天表
void ChatServer::createChatTable()
{
    QSqlQuery sq;
    QString sql = "CREATE TABLE IF NOT EXISTS chat ("
                  "dt TEXT NOT NULL, "
                  "nickname TEXT NOT NULL, "
                  "msg TEXT NOT NULL)";

    if (!sq.exec(sql)) {
        printMsg("创建聊天记录表失败: " + sq.lastError().text());
    } else {
        printMsg("聊天记录表创建/检查完成");
    }
}

// 处理新客户端连接
void ChatServer::newConnectionSlot()
{
    // 获取客户端套接字
    // nextPendingConnection() 返回待处理的客户端连接
    QTcpSocket *socket = server->nextPendingConnection();

    // 检查最大连接数
    if (clients.size() >= 20) {
        printMsg("客户端连接数已达上限(20)，拒绝新连接: " + getClientInfo(socket));
        socket->write("服务器繁忙，请稍后再试\n");
        socket->disconnectFromHost();//主动断开连接
        return;
    }

    // 客户端管理
    clients.append(socket);
    printMsg("新客户端连接: " + getClientInfo(socket) + " | 当前连接数: " + QString::number(clients.size()));

    // 信号槽连接`
    // 这些连接会在套接字销毁时自动断开
    // 当客户端发送数据时触发，绑定到 readyReadSlot 处理消息
    connect(socket, &QTcpSocket::readyRead, this, &ChatServer::readyReadSlot);
    // disconnected：当客户端断开时触发，绑定到 disconnSlot 清理资源
    connect(socket, &QTcpSocket::disconnected, this, &ChatServer::disconnSlot);

    // 发送欢迎消息
    socket->write("欢迎来到聊天室服务器!\n");
}
//处理消息
void ChatServer::readyReadSlot()
{
    // 通过 sender() 获取信号发射者（即触发 readyRead 的客户端套接字）
    // qobject_cast 确保类型安全，失败时直接返回
    QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
    if (!socket) return;

    // 按行读取数据
    // canReadLine() 检查是否有完整一行数据（依赖 \n 判断行尾）
    while (socket->canReadLine()) {
        // readLine() 读取单行数据并转换为UTF-8字符串
        // trimmed() 移除首尾空白字符（如多余的 \r\n）
        QString line = QString::fromUtf8(socket->readLine()).trimmed();
        printMsg("收到来自 " + getClientInfo(socket) + " 的消息: " + line);

        // 消息解析
        // 按 |分割消息为多个字段
        QStringList parts = line.split("|");
        if (parts.isEmpty()) continue;

        // 处理不同类型的消息
        if (parts[0] == "REGISTER" && parts.size() == 3) {
            // 注册: REGISTER:username:password
            QString username = parts[1];
            // 密码使用SHA-256哈希存储（避免明文保存）
            QString password = QCryptographicHash::hash(parts[2].toUtf8(),
                                                        QCryptographicHash::Sha256).toHex();
            // 检查用户名是否存在 → 数据库插入
            if (isDataExists(username)) {
                socket->write("REGISTER|FAIL|用户名已存在\n");
            } else {
                QSqlQuery sq;
                sq.prepare("INSERT INTO login_msg VALUES(?, ?)");
                sq.addBindValue(username);
                sq.addBindValue(password);

                if (sq.exec()) {
                    socket->write("REGISTER|SUCCESS\n");
                } else {
                    socket->write("REGISTER|FAIL|数据库错误\n");
                }
            }
        }
        else if (parts[0] == "LOGIN" && parts.size() == 3) {
            // 登录: LOGIN:username:password
            QString username = parts[1];
            QString password = QCryptographicHash::hash(parts[2].toUtf8(),
                                                        QCryptographicHash::Sha256).toHex();
            if (isLoginExists(username, password)) {
                socket->write("LOGIN|SUCCESS\n");
            } else {
                socket->write("LOGIN|FAIL|用户名或密码错误\n");
            }
        }
        else if (parts[0] == "MESSAGE" && parts.size() >= 3) {
            // 消息: MESSAGE:nickname:message content
            QString nickname = parts[1];
            QString message = parts.mid(2).join(":");
            QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");

            // 保存到数据库
            QSqlQuery sq;
            sq.prepare("INSERT INTO chat VALUES(?, ?, ?)");
            sq.addBindValue(timestamp);
            sq.addBindValue(nickname);
            sq.addBindValue(message);

            if (sq.exec()) {
                // 广播消息给所有客户端
                QString broadcastMsg = QString("MESSAGE|%1|%2|%3\n").arg(timestamp,nickname,message);

                // 遍历 clients 列表发送给其他在线用户
                foreach (QTcpSocket *client, clients) {
                    if (client != socket && client->state() == QTcpSocket::ConnectedState) {
                        client->write(broadcastMsg.toUtf8());
                    }
                }

                socket->write("MESSAGE|ACK\n");
            } else {
                socket->write("MESSAGE|FAIL|无法保存消息\n");
            }
        }
        else if (parts[0] == "QUERY_TIME" && parts.size() == 2) {
            // 按时间查询: QUERY_TIME:timestamp
            selectTime(parts[1], clients.indexOf(socket));
        }
        else if (parts[0] == "QUERY_NICKNAME" && parts.size() == 2) {
            // 按昵称查询: QUERY_NICKNAME:nickname
            selectNickname(parts[1], socket);
        }
        else if (parts[0] == "QUERY_MSG" && parts.size() == 2) {
            // 按消息内容查询: QUERY_MSG:keyword
            selectMsg(parts[1], socket);
        }
        else if (parts[0] == "QUERY_ALL") {
            // 查询所有消息: QUERY_ALL
            selectAll(socket);
        }
        else {
            socket->write("ERROR|未知命令格式\n");
        }
    }
}
//断开连接
void ChatServer::disconnSlot()
{
    // 获取发送者套接字
    QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
    if (!socket) return;
    // removeOne：删除第一个匹配的套接字（确保不重复删除）
    clients.removeOne(socket);
    //打印日志
    printMsg("客户端断开连接: " + getClientInfo(socket) + " | 剩余连接数: " + QString::number(clients.size()));
    //释放资源
    // deleteLater()：
    // 安全延迟删除（Qt事件循环下一周期释放）
    // 避免立即删除导致正在处理的事件崩溃
    // 自动触发套接字的析构流程
    socket->deleteLater();
}

//检查用户名
bool ChatServer::isDataExists(const QString &id)
{
    QSqlQuery sq;
    sq.prepare("SELECT 1 FROM login_msg WHERE user_name = ? LIMIT 1");
    sq.addBindValue(id);
    // sq.exec()：执行SQL查询，成功返回 true，失败返回 false。
    // sq.next()：尝试移动到查询结果的第一条记录。如果存在记录返回 true，否则 false。
    // 逻辑与：只有查询成功 且 存在记录时，函数才返回 true。
    return sq.exec() && sq.next();
}
// 检查用户信息
bool ChatServer::isLoginExists(const QString &user_name, const QString &password)
{
    QSqlQuery sq;
    sq.prepare("SELECT 1 FROM login_msg WHERE user_name = ? AND password = ? LIMIT 1");
    sq.addBindValue(user_name);
    sq.addBindValue(password);
    return sq.exec() && sq.next();
}
//按时间查询消息
void ChatServer::selectTime(const QString &dt, int clientIndex)
{
    // 确保 clientIndex 在合法范围内（避免越界访问）。
    if (clientIndex < 0 || clientIndex >= clients.size()) return;
    // 获取客户端套接字
    QTcpSocket *socket = clients.at(clientIndex);

    // 准备SQL查询
    QSqlQuery sq;
    // SELECT * FROM chat：查询 chat 表的所有字段。
    // WHERE dt LIKE ?：模糊匹配 dt 字段，? 是占位符。
    // ORDER BY dt：按 dt 字段升序排序
    sq.prepare("SELECT * FROM chat WHERE dt LIKE ? ORDER BY dt DESC");
    // 参数绑定：sq.addBindValue(dt + "%")：将 dt 加上通配符 %（如 "2023-10-01%"），匹配以 dt 开头的所有时间。
    sq.addBindValue(dt + "%");

    // 执行查询并处理结果
    if (sq.exec()) {
        while (sq.next()) {
            QString result = QString("QUERY_RESULT|TIME|%1|%2|%3\n").arg(sq.value(0).toString(),sq.value(1).toString(),sq.value(2).toString());
            // 通过套接字发送给客户端查询结果
            socket->write(result.toUtf8());
        }
        socket->write("QUERY_RESULT|TIME|END\n");
    } else {
        socket->write("QUERY_ERROR|TIME|查询失败\n");
    }
}
//按昵称查询消息
void ChatServer::selectNickname(const QString &nickname, QTcpSocket *clientSocket)
{
    if (!clientSocket) return;

    QSqlQuery sq;
    sq.prepare("SELECT * FROM chat WHERE nickname = ? ORDER BY dt DESC");
    sq.addBindValue(nickname);

    if (sq.exec()) {
        while (sq.next()) {
            QString result = QString("QUERY_RESULT|NICKNAME|%1|%2|%3\n").arg(sq.value(0).toString(),sq.value(1).toString(),sq.value(2).toString());
            clientSocket->write(result.toUtf8());
        }
        clientSocket->write("QUERY_RESULT|NICKNAME|END\n");
    } else {
        clientSocket->write("QUERY_ERROR|NICKNAME|查询失败\n");
    }
}
//按消息查询消息
void ChatServer::selectMsg(const QString &msg, QTcpSocket *clientSocket)
{
    if (!clientSocket) return;

    QSqlQuery sq;
    sq.prepare("SELECT * FROM chat WHERE msg LIKE ? ORDER BY dt DESC");
    // 参数绑定： sq.addBindValue("%" + msg + "%")：将 msg 前后加上通配符 %（如 "%hello%"），匹配包含 msg 的所有消息。
    sq.addBindValue("%" + msg + "%");

    if (sq.exec()) {
        while (sq.next()) {
            QString result = QString("QUERY_RESULT|MSG|%1|%2|%3\n").arg(sq.value(0).toString(),sq.value(1).toString(),sq.value(2).toString());
            clientSocket->write(result.toUtf8());
        }
        clientSocket->write("QUERY_RESULT|MSG|END\n");
    } else {
        clientSocket->write("QUERY_ERROR|MSG|查询失败\n");
    }
}
//查询所有消息
void ChatServer::selectAll(QTcpSocket *clientSocket)
{
    if (!clientSocket) return;

    QSqlQuery sq("SELECT * FROM chat ORDER BY dt DESC");

    if (sq.exec()) {
        while (sq.next()) {
            QString result = QString("QUERY_RESULT|ALL|%1|%2|%3\n").arg(sq.value(0).toString(),sq.value(1).toString(),sq.value(2).toString());
            clientSocket->write(result.toUtf8());
        }
        clientSocket->write("QUERY_RESULT|ALL|END\n");
    } else {
        clientSocket->write("QUERY_ERROR|ALL|查询失败\n");
    }
}

//通过套接字获取客户端信息
QString ChatServer::getClientInfo(QTcpSocket *socket) const
{
    if (!socket) return "Unknown";
    // 将客户端的网络连接信息格式化为易读的字符串
    // peerAddress()：获取客户端的 IP地址（返回 QHostAddress）
    // toString()：转为字符串（如 "192.168.1.100"）
    // peerPort()：获取客户端的 端口号（如 54321）
    return QString("%1|%2").arg(socket->peerAddress().toString()).arg(socket->peerPort());
}
